package com.jnevision.clockin.controller;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLDecoder;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.api.ApiController;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jnevision.clockin.model.AttendanceRecordVO;
import com.jnevision.clockin.model.ClockIn;
import com.jnevision.clockin.service.ClockInServiceImpl;
import com.jnevision.common.CommonQuery;
import com.jnevision.dept.model.Dept;
import com.jnevision.dept.service.DeptServiceImpl;
import com.jnevision.dict.model.Dict;
import com.jnevision.dict.service.DictServiceImpl;
import com.jnevision.extra.service.ExtraServiceImpl;
import com.jnevision.salary.service.SalaryServiceImpl;
import com.jnevision.salaryrecord.model.SalaryRecord;
import com.jnevision.salaryrecord.service.SalaryRecordServiceImpl;
import com.jnevision.sysconfig.service.ConfigServiceImpl;
import com.jnevision.user.model.User;
import com.jnevision.user.service.UserServiceImpl;
import com.jnevision.util.DateUtil;
import com.jnevision.util.Ranging;

/**
 * <p>
 * 工作打卡记录 前端控制器
 * </p>
 *
 * @author gavin
 * @since 2019-07-13
 */

@RestController
@RequestMapping("/clockins")
public class ClockInController extends ApiController {
    @Autowired
    private ClockInServiceImpl clockInServiceImpl;
    @Autowired
    private UserServiceImpl userServiceImpl;
    @Autowired
    private DeptServiceImpl deptServiceImpl;
    @Autowired
    private ConfigServiceImpl configServiceImpl;
    @Autowired
    private SalaryRecordServiceImpl salaryRecordServiceImpl;
    @Autowired
    private SalaryServiceImpl salaryServiceImpl;
    @Autowired
    private ExtraServiceImpl extraServiceImpl;
    @Autowired
    private DictServiceImpl dictServiceImpl;

    /**
     * 上班打卡 <br>
     * 因为有些员工不使用自己的账号，所以使用脸部识别码
     * 
     * @param clockIn
     * @param token
     * @return
     */
    @PostMapping
    public R<User> add(@RequestBody ClockIn clockIn, @RequestHeader("token") String token) {
        if (this.clockInServiceImpl.getOne(new QueryWrapper<ClockIn>().eq("face_code", clockIn.getFaceCode())
            .eq("work_day", LocalDate.now().toString())) != null) {
            return this.failed("今日已经签到了，无需重复签到");
        }
        User user = this.userServiceImpl.getOne(new QueryWrapper<User>().eq("face_code", clockIn.getFaceCode()));
        if (user == null) {
            return this.failed("签到失败，没有此用户信息");
        }
        String job = this.dictServiceImpl.getOne(new QueryWrapper<Dict>().eq("type", "job").eq("value", user.getJob()))
            .getLabel();
        user.setJob(job);

        String position = this.deptServiceImpl.getProject(token).getPosition();

        if (StringUtils.isNotBlank(position) && !position.contains("4.9E-324")) {
            double rang = Ranging.getDistance(position, clockIn.getWorkPlace());
            String distance = this.configServiceImpl.getValue("push_card_distance");
            if (rang > Double.valueOf(distance)) {
                user.setCardStatus("外勤签到成功");
            } else {
                user.setCardStatus("签到成功");
            }
        } else {
            user.setCardStatus("签到成功");
        }

        clockIn.setWorkDay(LocalDate.now().toString());
        clockIn.setUserId(user.getId());
        clockIn.setWorkShift(DateUtil.getNormalTime());
        this.clockInServiceImpl.save(clockIn);

        return this.success(user);
    }

    /**
     * 下班打卡 <br>
     * 因为有些员工不使用自己的账号，所以使用脸部识别码
     * 
     * @param clockIn
     * @param token
     * @return
     */
    @PutMapping
    public R<User> update(@RequestBody ClockIn clockIn, @RequestHeader("token") String token) {
        User user = this.userServiceImpl.getOne(new QueryWrapper<User>().eq("face_code", clockIn.getFaceCode()));
        if (user == null) {
            return this.failed("签退失败，没有此用户信息");
        }
        String job = this.dictServiceImpl.getOne(new QueryWrapper<Dict>().eq("type", "job").eq("value", user.getJob()))
            .getLabel();
        user.setJob(job);

        ClockIn shift = this.clockInServiceImpl.getOne(new QueryWrapper<ClockIn>()
            .eq("face_code", clockIn.getFaceCode()).eq("work_day", clockIn.getClosingTime().substring(0, 10)));

        if (shift == null) {
            shift = clockIn;
        } else {
            shift.setClosingTime(DateUtil.getNormalTime());
            shift.setOffPlace(clockIn.getOffPlace());
        }
        shift.setUserId(user.getId());

        String position = this.deptServiceImpl.getProject(token).getPosition();

        if (StringUtils.isNotBlank(position) && !position.contains("4.9E-324")) {
            double rang = Ranging.getDistance(position, clockIn.getOffPlace());
            String distance = this.configServiceImpl.getValue("push_card_distance");
            if (rang > Double.valueOf(distance)) {
                user.setCardStatus("外勤签退成功");
            } else {
                user.setCardStatus("签退成功");
            }
        } else {
            user.setCardStatus("签退成功");
        }

        this.clockInServiceImpl.saveOrUpdate(shift);

        return this.success(user); // 更新或保存，是因为有可能上午不打卡，下午打卡
    }

    /**
     * 
     * @param token
     * @param current
     * @return true 是正常范围，false 是外勤
     */
    @GetMapping("/isnormal")
    public R<Boolean> isNormal(@RequestHeader("token") String token, String current) {
        try {
            current = URLDecoder.decode(current, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        if (StringUtils.isBlank(current) || StringUtils.trim(current).split(",").length < 2
            || current.contains("4.9E-324")) {
            return this.success(true);
        }

        Dept dept = this.deptServiceImpl.getProject(token);
        double rang = Ranging.getDistance(current, dept.getPosition());
        String distance = this.configServiceImpl.getValue("push_card_distance");
        return this.success(rang < Double.valueOf(distance));
    }

    /**
     * 获取最后一次打卡信息<br>
     * 因为有些员工不使用自己的账号，所以使用脸部识别码
     * 
     * @param token
     * @return
     */
    @GetMapping
    public R<ClockIn> get(String faceCode) {
        // User user = JsonMapper.nonDefaultMapper().fromJson(this.redisTemplate.boundValueOps(token).get(),
        // User.class);
        User user = this.userServiceImpl.getOne(new QueryWrapper<User>().eq("face_code", faceCode));
        return this.success(this.clockInServiceImpl
            .getOne(new QueryWrapper<ClockIn>().eq("user_id", user.getId()).orderByDesc("work_shift").last("limit 1")));
    }

    /**
     * 考勤统计
     * 
     * @param yearMonth 年月 yyyy-MM
     * @return
     */
    @GetMapping("/statistics")
    public R<List<ClockIn>> statistics(String yearMonth, @RequestHeader("token") String token) {
        return this.success(this.clockInServiceImpl.list(
            new QueryWrapper<ClockIn>().between("work_day", yearMonth + "-01", DateUtil.getNextMonth(yearMonth) + "-01")
                .eq("user_id", this.userServiceImpl.getLoginUser(token).getId())));
    }

    // 可以根据考勤规则，在app上打卡时，显示当前的状态，比如迟到，请假，早退 TODO 是否需要显示
    // 查询 旷工的详细，迟到的详细
    /**
     * 
     * @param yearMonth
     * @return 0 正常， 1 迟到， 3 缺卡， 4 早退， 2 请假， 5 旷工
     */
    @GetMapping("/collect")
    public R<Map<String, Map<String, String>>> collect(String yearMonth, @RequestHeader("token") String token) {
        List<ClockIn> clockList = this.clockInServiceImpl.list(
            new QueryWrapper<ClockIn>().between("work_day", yearMonth + "-01", DateUtil.getNextMonth(yearMonth) + "-01")
                .eq("user_id", this.userServiceImpl.getLoginUser(token).getId()));
        User user = this.userServiceImpl.getLoginUser(token);
        String start = yearMonth + "-01";
        String end = DateUtil.getNextMonth(yearMonth) + "-01";

        final Map<String, String> map = new HashMap<>();
        final Map<String, String> map2 = new LinkedHashMap<>();
        final Map<String, Map<String, String>> retMap = new HashMap<>();

        MultiValueMap multiMap = new MultiValueMap();
        for (ClockIn clockIn : clockList) {
            multiMap.put(clockIn.getClockInStatus(), clockIn);
        }

        multiMap.remove(8); // 去除休息

        int normal = 0, later = 0, missCard = 0;
        for (Object status : multiMap.keySet()) {
            if (status.equals(0) || status.equals(7)) {
                normal += multiMap.getCollection(status).size();
            } else if (status.equals(1) || status.equals(6)) {
                later += multiMap.getCollection(status).size();
            } else if (status.equals(3) || status.equals(9)) {
                missCard += multiMap.getCollection(status).size();
            } else {
                map.put(status.toString(), String.valueOf(multiMap.getCollection(status).size()));
            }
        }

        map.put("0", String.valueOf(normal)); // 正常
        map.put("1", String.valueOf(later)); // 迟到
        map.put("3", String.valueOf(missCard)); // 缺卡

        // 11(岗位+保障)，12工龄工资，13它得，14加班，15保险扣除，16请假扣除，17实发工资
        SalaryRecord record = this.salaryRecordServiceImpl
            .getOne(new QueryWrapper<SalaryRecord>().eq("user_id", user.getId()).eq("date", yearMonth));

        if (record == null) {
            map2.put("基本工资", "0");
            map2.put("保险扣除", "0");
            map2.put("工龄工资", "0");
            map2.put("请假扣除", "0");
            map2.put("它得金额", "0");
            map2.put("加班工资", "0");
            map2.put("实发工资", "0");
        } else {
            BigDecimal shifa = new BigDecimal(0);
            shifa = shifa.add(new BigDecimal(this.dictServiceImpl.getLabel("baozhang", record.getBaozhang()))
                .add(new BigDecimal(record.getGangwei())));
            map2.put("基本工资", shifa.toString());
            map2.put("保险扣除", new BigDecimal(record.getYiliao()).add(new BigDecimal(record.getYanglao()))
                .add(new BigDecimal(record.getShiye()).setScale(2, RoundingMode.HALF_UP)).toString());

            map2.put("工龄工资", record.getGongling());
            map2.put("请假扣除", this.salaryServiceImpl.calcLeaveFee(user.getId(), start, end).toString());

            CommonQuery query = new CommonQuery();
            query.setUserId(user.getId());
            query.setStart(start);
            query.setEnd(end);
            String extra = Optional.ofNullable(this.extraServiceImpl.countUserExtra(query)).orElse(new String("0"));

            map2.put("它得金额", extra);
            map2.put("加班工资", this.salaryServiceImpl.calcOvertimeFee(user.getId(), start, end).toString());

            shifa = shifa.add(new BigDecimal(record.getGongling())).add(new BigDecimal(map2.get("它得金额")))
                .add(new BigDecimal(map2.get("加班工资")));
            shifa = shifa.subtract(new BigDecimal(map2.get("保险扣除")).add(new BigDecimal(map2.get("请假扣除"))));
            map2.put("实发工资", shifa.toString());
        }

        retMap.put("clockin", map);
        retMap.put("salary", map2);

        return this.success(retMap);
    }

    /**
     * 当前登录用户获取打卡记录
     * 
     * @param page
     * @param token
     * @return
     */
    @GetMapping("/page")
    public R<IPage<ClockIn>> page(Page<ClockIn> page, @RequestHeader("token") String token) {
        String userId = this.userServiceImpl.getLoginUser(token).getId();

        return this.success(this.clockInServiceImpl.page(page, userId));
    }

    /**
     * pc考勤记录
     * 
     * @param page
     * @param query
     * @return
     */
    @GetMapping("/attendance/record")
    public R<IPage<AttendanceRecordVO>> attendanceRecord(Page<AttendanceRecordVO> page, CommonQuery query) {
        query.setEnd(DateUtil.getNextMonth(query.getStart()) + "-01");
        query.setStart(query.getStart() + "-01");
        return this.success(this.clockInServiceImpl.attendanceRecord(page, query));
    }

    /**
     * pc考勤记录
     * 
     * @param page
     * @param query
     * @return
     */
    @GetMapping("/attendance/record/page")
    public R<IPage<ClockIn>> recordPage(Page<ClockIn> page, CommonQuery query) {
        query.setEnd(DateUtil.getNextMonth(query.getStart()) + "-01");
        query.setStart(query.getStart() + "-01");
        return this.success(this.clockInServiceImpl.recordDetail(page, query));
    }
}
